<?php

namespace App\Services\Request;

use App\Tools\Json;
use App\Tools\Signature;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log;

abstract class BaseRequest
{
    /**
     * 接口基础url
     * @var string
     */
    public string $baseUri;

    /**
     * 业务侧响应成功状态码,默认0表示成功,非0则失败
     * @var int
     */
    public int $successCode = 0;
    /**
     * 错误信息字段名
     * @var string
     */
    public string $errorMsgField = 'error';

    /**
     * 请求超时时间
     * @var int
     */
    public int $timeout = 5;

    /**
     * 是否开启调试
     * @var bool
     */
    public bool $debug = false;

    /**
     * 是否自动加签
     * @var bool
     */
    public bool $sign = true;

    /**
     * 请求头
     * @var array
     */
    public array $headers = [];

    /**
     * 初始化guzzle的配置
     * @var array
     */
    public array $clientConfig = [];

    /**
     * 响应的header头
     * @var array
     */
    public array $responseHeader = [];


    /**
     * 接口path
     * @var string
     */
    private string $uri;

    /**
     * 请求参数
     * @var array
     */
    private array $params = [];

    /**
     * 请求方式
     * @var string
     */
    private string $method = 'get';

    /**
     * 响应结果
     * @var mixed
     */
    private mixed $content = [];

    /**
     * 原始响应结果
     * @var mixed
     */
    private mixed $body = '';

    /**
     * GuzzleHttp实例
     * @var Client
     */
    private Client $client;

    /**
     * 响应实例
     * @var
     */
    private mixed $response;

    /**
     * 设置请求基础url
     * @return string
     */
    abstract public function setBaseUri(): string;

    /**
     * 构建请求
     * @param string $url
     * @param array $params
     * @param string $method
     * @return mixed
     * @throws \Exception
     */
    protected function buildRequest(string $url, array $params, string $method = 'post'): mixed
    {
        try {
            $this->setUri($url);
            $this->setMethod($method);
            $this->setParams($params);
            $this->exec();
        } catch (\Throwable $e) {
            throw new \Exception($e->getMessage(), $e->getCode());
        }

        return $this->getContent();
    }

    /**
     * 请求通道
     * @return $this
     * @throws \Throwable
     */
    public function exec(): self
    {
        $this->initBaseUri();
        $this->initClient();
        $this->requestWithSign();
        $this->request();
        $this->parseResponse();

        return $this;
    }

    /**
     * 初始化请求基础url
     * @throws \Exception
     */
    public function initBaseUri()
    {
        if (empty($this->setBaseUri()) && empty($this->baseUri)) {
            throw new \Exception('请设置请求基础url');
        }

        if (empty($this->baseUri)) {
            $this->baseUri = $this->setBaseUri();
        }
    }

    /**
     * 初始化请求
     * @return void
     */
    public function initClient()
    {
        // 可根据后续需要扩展参数
        $this->client = new Client(array_merge([
            'verify' => false,
            'base_uri' => $this->baseUri,
            'debug' => $this->debug,
            'timeout' => $this->timeout,
            'headers' => $this->headers,
        ], $this->clientConfig));
    }

    /**
     * 对请求加签
     * @return void
     */
    public function requestWithSign()
    {
        if ($this->sign) {
            $this->params = array_merge($this->params, [
                'sign' => Signature::generate($this->params, $this->rpcSignSecret()),
            ]);
        }
    }

    /**
     * 获取签名secret
     * @return string
     */
    public function rpcSignSecret(): string
    {
        return config('rpc_request.common_rpc_key');
    }

    /**
     * 执行请求
     * @return void
     * @throws \GuzzleHttp\Exception\GuzzleException
     * @throws \Throwable
     */
    public function request(): void
    {
        try {
            $log = [
                'base_url' => $this->baseUri,
                'url' => $this->uri,
                'headers' => $this->headers,
                'params' => $this->params,
                'method' => $this->method,
            ];
            //请求前
            Log::info('request_before', $log);
            $this->response = match (strtolower($this->method)) {
                'get' => $this->client->get($this->uri, ['query' => $this->params]),
                'post' => $this->client->post($this->uri, ['form_params' => $this->params]),
                'multipart' => $this->client->post($this->uri, ['multipart' => $this->params]),
                'json' => $this->client->post($this->uri, ['json' => $this->params]),
                default => throw new \Exception('Unsupported request method: ' . $this->method),
            };
            if (empty($this->clientConfig['stream'])) {
                //原始响应
                $this->body = $this->response->getBody()->getContents();
                //响应header头
                $this->responseHeader = $this->response->getHeaders();
                //格式化原始响应
                // 验证 $this->body 是否是合法的json字符串格式
                if (json_decode($this->body, true) === null && json_last_error() !== JSON_ERROR_NONE) {
                    $result = $this->body;
                } else {
                    $result = json_decode($this->body, true);
                }
                
                $this->content = $result ?? [];
                //请求后
                Log::info('request_after', array_merge($log, ['body' => $this->body, 'content' => $this->content]));
            } else {
                $this->content = $this->response;
            }
        } catch (\Throwable $exception) {
            $log = [
                'base_url' => $this->baseUri,
                'url' => $this->uri,
                'headers' => $this->headers,
                'params' => $this->params,
                'method' => $this->method,
                'body' => $this->body,
                'content' => $this->content,
                'message' => $exception->getMessage(),
            ];
            Log::error('request_error', $log);

            throw $exception;
        }
    }

    /**
     * 处理响应
     * @return void
     * @throws \Exception
     */
    public function parseResponse()
    {
        try {
            $result = $this->getContent();

            //自由处理业务响应的错误示例:
            if (!isset($result['code']) || $result['code'] !== $this->successCode) {
                throw new \Exception($result[$this->errorMsgField] ?? '未知错误');
            }

            $this->content = $result['data'] ?? [];
        } catch (\Throwable $exception) {
            Log::error('request_response_error', [
                'base_uri' => $this->getBaseUri(),
                'url' => $this->getUri(),
                'headers' => $this->getHeaders(),
                'params' => $this->getParams(),
                'method' => $this->getMethod(),
                'body' => $this->getBody(),
                'content' => $this->getContent(),
                'message' => $exception->getMessage(),
            ]);

            throw $exception;
        }
    }

    /**
     * 设置当前请求超时时间
     * @param int $timeout
     * @return $this
     */
    public function setTimeOut(int $timeout): static
    {
        $this->timeout = $timeout;

        return $this;
    }

    /**
     * 设置当前请求头
     * @param array $headers
     * @return $this
     */
    public function setHeaders(array $headers = []): static
    {
        $this->headers = $headers;

        return $this;
    }

    /**
     * 设置guzzle请求注入client的config配置
     * @param array $clientConfig
     * @return $this
     */
    public function setClient(array $clientConfig = []): static
    {
        $this->clientConfig = $clientConfig;

        return $this;
    }

    /**
     * 设置请求path
     * @param $uri
     * @return $this
     */
    public function setUri($uri): static
    {
        $this->uri = $uri;

        return $this;
    }

    /**
     * 设置请求参数
     * @param $params
     * @return $this
     */
    public function setParams($params): static
    {
        $this->params = $params;

        return $this;
    }

    /**
     * 设置请求方式
     * @param $method
     * @return $this
     */
    public function setMethod($method): static
    {
        $this->method = $method;

        return $this;
    }

    /**
     * 获取接口基础url
     * @return string
     */
    public function getBaseUri(): string
    {
        return $this->baseUri;
    }

    /**
     * 获取请求path
     * @return string
     */
    public function getUri(): string
    {
        return $this->uri;
    }

    /**
     * 获取请求头
     * @return array
     */
    public function getHeaders(): array
    {
        return $this->headers;
    }

    /**
     * 获取请求参数
     * @return array
     */
    public function getParams(): array
    {
        return $this->params;
    }

    /**
     * 获取请求方式
     * @return string
     */
    public function getMethod(): string
    {
        return $this->method;
    }

    /**
     * 获取原始响应
     * @return mixed
     */
    public function getBody(): mixed
    {
        return $this->body;
    }

    /**
     * 获取响应结果
     * @return mixed
     */
    public function getContent(): mixed
    {
        return $this->content;
    }

    /**
     * 获取响应的header
     * @return array
     */
    public function getResponseHeader(): array
    {
        return $this->responseHeader;
    }

    public function setContent($content)
    {
        $this->content = $content;
    }
}
